package com.codingapi.websocket.core.server;

import com.codingapi.websocket.core.config.ServerConfig;
import com.codingapi.websocket.core.handler.WebSocketChannelHandlerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author modificial
 * @date 2018/7/19
 * @company codingApi
 * @description webSocket服务启动
 */
@Component
public class WebSocketNettyServer {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketNettyServer.class);
    /**
     * 手动创建线程池
     */
    private final ThreadLocal<ExecutorService> executorService = ThreadLocal.withInitial(Executors::newSingleThreadExecutor);
    /**
     * webSocket处理器工厂
     */
    private WebSocketChannelHandlerFactory webSocketChannelHandlerFactory = new WebSocketChannelHandlerFactory();
    @Autowired
    private ServerConfig serverConfig;

    /**
     * 启动服务
     */
    public void startNettyServer() {

        executorService.get().execute(() -> {
            EventLoopGroup boss = new NioEventLoopGroup();
            EventLoopGroup worker = new NioEventLoopGroup(serverConfig.getWorkerCount());
            try {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(boss, worker);
                bootstrap.channel(NioServerSocketChannel.class);
                //连接数
                bootstrap.option(ChannelOption.SO_BACKLOG, serverConfig.getBacklog());
                //不延迟，消息立即发送
                bootstrap.option(ChannelOption.TCP_NODELAY, serverConfig.isTcpNodelay());
                //超时时间
                bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000);
                //长连接
                bootstrap.childOption(ChannelOption.SO_KEEPALIVE, serverConfig.isKeepalive());
                bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) {
                        ChannelPipeline p = socketChannel.pipeline();

                        //HttpServerCodec: 针对http协议进行编解码
                        p.addLast("http-codec", new HttpServerCodec());
                        /**
                         * 作用是将一个Http的消息组装成一个完成的HttpRequest或者HttpResponse，那么具体的是什么
                         * 取决于是请求还是响应, 该Handler必须放在HttpServerCodec后的后面
                         */
                        p.addLast("aggregator", new HttpObjectAggregator(65536));
                        /**
                         *  ChunkedWriteHandler分块写处理，文件过大会将内存撑爆
                         */
                        p.addLast("http-chunked", new ChunkedWriteHandler());
                        /**
                         * 请求处理
                         */
                        p.addLast("inboundHandler", webSocketChannelHandlerFactory.newWSInboundHandler());
                        /**
                         * 关闭处理
                         */
                        p.addLast("outboundHandler", webSocketChannelHandlerFactory.newWSOutboundHandler());

                    }
                });

                ChannelFuture f = bootstrap.bind(serverConfig.getPort()).sync();
                if (f.isSuccess()) {
                    LOGGER.info("连接成功");
                }
                f.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
            }
        });


    }

    @PostConstruct
    public void init() {
        //启动服务
        LOGGER.info("WebSocket服务开始启动");
        startNettyServer();
    }

}
